#ifndef AppImpl_H_
#define AppImpl_H_
#include "dbg.h"
#include "fv_threads.h"
#include "fv_config.h"
#include "pch_intf.h"
#include "WindowFV.h"

namespace FemViewer {

/* The instatnt type */
enum InstantType {
	MODULE,
	APPLICATION
};

/* Types of module */
enum CoreType {
	ALONE = 0,
	MASTER,
	SLAVE,
	COUNT
};

/* Forward declaration of start function */
template< typename T,CoreType Type > struct Start;

/* Base class */
class Base {
protected:
  //static Base * _self;
  bool   _run;
//public:
  // run parameters
  int    _argc;
  char** _argv;
  FILE*  _out;
protected:
  // ctrs
  Base(void)
  : _run(false), _argc(1), _argv(0), _out(0)
  { /*_self = this;*/ }
  Base(int argc, char **argv,FILE *out)
  : _run(false), _argc(argc), _argv(argv), _out(out)
  { /*_self = this;*/
	  //mfp_debug("Base ctr\n");
  }
  // dtr
  virtual ~Base(void) { /*_self = 0;*/ }
  // friends
  template< typename T1, CoreType Type1> friend struct Start;
};

/* Core class for application */
template< typename T >
class Core : public Base {
	typedef T Kernel;
  public:
	static int init(int argc,char **argv,FILE *out) {
		mf_debug("W inicjajcji2\n");
		return Kernel::Init(argc,argv,out);
	}
	Core(void){}
	~Core(void){}
};

/* Partial specialization of Core for module */
template< typename T >
class Core<T*> : public Base {
	typedef T Kernel;
	typedef Kernel * pKernel;
	static Core* _selfp;

public:
	static int init(int argc,char **argv,FILE *out);
	static void close(void);

public:
  	~Core(void) {
  		DELETECRITICALSECTION(_crtsec);
  		DELETESIGNAL(_signal);
  	}

  private:
	fv_thread 			_handle;
	fv_thread_id 		_id;

  public:
	fv_critical_section _crtsec;
	fv_signal_t			_signal;


  private:
	Core(int argc,char **argv,FILE *out)
    : Base(argc,argv,out)
    , _handle(0)
    , _id(0)
    {
		mfp_debug("Core ctr\n");
		atexit(close);
		INITCRITICALSECTION(_crtsec);
		INITSIGNAL(_signal);
    }
private:
	// Not implemented
	Core(const Core<T*>&);
	Core<T*>& operator=(const Core<T*>&);

};


/* Start function for STAND_ALONE module */
template< typename T >
struct Start<T,ALONE> {
	static fv_thread_return run(void *pParams) {
		Core<T*> * lptr = static_cast<Core<T*> *>(pParams);
		T::Init(lptr->_argc,lptr->_argv, lptr);
		ENTERCRITICALSECTION(lptr->_crtsec);
		mfp_debug("Wake up main thread\n");
		fv_wakeup(&(lptr->_signal));
		LEAVECRITICALSECTION(lptr->_crtsec);
		return (fv_thread_return)0;
	}
};

/* Start function for PARALLEL_MASTER module */
template< typename T >
struct Start<T,MASTER> {
	static fv_thread_return run(void *pParams) {
		Core<T> * lptr = static_cast<Core<T> *>(pParams);
		T::Init(lptr->_argc,lptr->_argv);
		return (fv_thread_return)0;
	}
};

/* Start function for PARALLEL_SLAVES modules */
template< typename T >
struct Start<T,SLAVE> {
	static fv_thread_return run(void *pParams) {
		Base * lptr = static_cast<Base *>(pParams);
		T::Init2(lptr->_argc,lptr->_argv);
		//mfvWindow::init(lptr->_argc,lptr->_argv);
		return (fv_thread_return)0;
	}
};



template<typename T>
Core<T*>* Core<T*>::_selfp = NULL;

template<typename T>
int Core<T*>::init(int argc,char **argv,FILE *out)
{
	// Try init
	if (_selfp == NULL) {
		try {
			_selfp = new Core(argc,argv,out);
		}
		catch (const std::bad_alloc & e) {
			std::string msg("Error during graphic kernel init: ");
			msg.append( e.what());
			msg.append( "\n");
			throw msg;
		} catch(...) {
			mfp_debug("Something is wrong in init render core!\n");
		}
	}

	// Start new thread
	if (_selfp->_handle == 0) {
#ifdef PARALLEL
		if (pcr_my_proc_id() == PCC_MASTER_PROC_ID) {
			//_selfp->_handle = (fv_thread)fv_start_thread(&_selfp->_id,Start<T,MASTER>::run,_selfp);
			return T::Init(_selfp->_argc,_selfp->_argv);
		}
		else {
			//_selfp->_handle = (fv_thread)fv_start_thread(&_selfp->_id,Start<T,SLAVE>::run,_selfp);
			return T::Init2(_selfp->_argc,_selfp->_argv);
		}
#else
		//mf_debug("In part\n");
		_selfp->_handle = (fv_thread)fv_start_thread(&_selfp->_id,Start<T,ALONE>::run,_selfp);
#endif
		}

	ENTERCRITICALSECTION(_selfp->_crtsec);
	mfp_debug("Waiting for slave\n");
	fv_wait_for_wakeup(&(_selfp->_signal),&(_selfp->_crtsec));
	mfp_debug("Waked up\n");
	LEAVECRITICALSECTION(_selfp->_crtsec);

	return static_cast<int>(_selfp->_handle);
}

template<typename T>
void Core<T*>::close(void) {
	mfp_debug("Core::close\n");
	if (_selfp != NULL && _selfp->_handle)
	{
		// Signal and of the work
		ENTERCRITICALSECTION(_selfp->_crtsec);
		_selfp->_run = false;
		LEAVECRITICALSECTION(_selfp->_crtsec);

		// Wait until thead is finished
		fv_end_thread(_selfp->_handle);

		// Sllep for one second and destroy kernel
		sleep(1);
		delete _selfp;
		_selfp = NULL;
	}
}


}// end namespace FemViewer

#endif /* AppImpl_H_ */
